#include "ESPTelnet.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include "ESP32FtpServer.h"
#include "ESPTelnet.h"
#include "spdlog/spdlog.h"
#include "network_log.h"
#include "../../hal/hal.h"
#include "app_log.h"

using namespace MOONCAKE::APPS;

// const char *ssid = "FLAME";         // WiFi SSID
const char *password = "12345678"; // WiFi Password
#define FTP_SERVER_USERNAME "esp32"
#define FTP_SERVER_PASSWORD "esp32"

// ESPTelnet telnet_can;
ESPTelnet telnet_uart;
ESPTelnet telnet_shell;

uint16_t telnet_can_port = 25;
uint16_t telnet_uart_port = 24;
uint16_t telnet_shell_port = 23;

int is_telnet_uart_connected = 0;
int is_telnet_shell_connected = 0;

FtpServer ftpSrv; // set #define FTP_DEBUG in ESP32FtpServer.h to see ftp verbose on serial

uint8_t g_netlog_status = 0;
uint16_t g_wifi_connect_cnt = 0;
uint16_t g_wifi_connect_time = 0;

void setupTelnet(void);

void network_log_init(void)
{
}

void network_log_handle(void)
{
    static uint32_t lastStamp_1 = 0;
    static uint32_t currStamp = 0;

    if (g_netlog_status == NETWWORK_STATE_WIFI_CONNECTED)
    {
        telnet_shell.loop();
        telnet_uart.loop();
        ftpSrv.handleFTP();
    }

    currStamp = millis();
    if (currStamp - lastStamp_1 > 1000)
    {
        lastStamp_1 = currStamp;

        // spdlog::info("g_netlog_status {}", g_netlog_status);

        switch (g_netlog_status)
        {
        case NETWWORK_STATE_IDLE:
            if ((WiFi.status() != WL_CONNECTED))
            {
                g_wifi_connect_cnt++;
                if (g_wifi_connect_cnt >= 5)
                {
                    g_wifi_connect_cnt = 0;
                    WiFi.mode(WIFI_STA);
                    WiFi.disconnect();
                    WiFi.begin(HAL::GetSystemConfig().wifi_ssid, password);
                    g_wifi_connect_time = 0;
                    g_netlog_status = NETWWORK_STATE_WIFI_CONNECTING;
                    spdlog::info("try to connect wifi, ssid:{}, password:{}", HAL::GetSystemConfig().wifi_ssid, password);
                }
            }
            else
            {
                g_wifi_connect_cnt = 0;
            }
            break;

        case NETWWORK_STATE_WIFI_CONNECTING:
            if ((WiFi.status() != WL_CONNECTED))
            {
                spdlog::info(".");
                g_wifi_connect_time++;
                if (g_wifi_connect_time > 10)
                {
                    g_wifi_connect_time = 0;
                    g_netlog_status = NETWWORK_STATE_IDLE;
                    spdlog::info("connect wifi timeout");
                }
            }
            else
            {
                g_wifi_connect_time = 0;
                g_netlog_status = NETWWORK_STATE_WIFI_CONNECTED;
                WiFi.setAutoReconnect(true);
                WiFi.persistent(true);

                IPAddress ip = WiFi.localIP();
                String ipStr = ip.toString();
                spdlog::info("Wifi Connect Sucessful, IP: {}", ip);

                setupTelnet();
                ftpSrv.begin(FTP_SERVER_USERNAME, FTP_SERVER_PASSWORD);
            }
            break;
        case NETWWORK_STATE_WIFI_CONNECTED:
            if ((WiFi.status() != WL_CONNECTED))
            {
                g_netlog_status = NETWWORK_STATE_IDLE;
            }
            break;
        default:
            break;
        }
    }
}

void onTelnetConnect_Uart(String ip)
{
    spdlog::info("- Telnet: {}, connected", ip.c_str());
    telnet_uart.println("\nWelcome, your ip is: " + telnet_uart.getIP() + " connect port: " + telnet_uart_port);
    telnet_uart.println("> cmd bye to disconnect.");
    telnet_uart.println("> type help and ctrl + enter to get help.");
    is_telnet_uart_connected = 1;
}

void onTelnetDisconnect_Uart(String ip)
{
    spdlog::info(" onTelnetDisconnect_Uart disconnected");
    is_telnet_uart_connected = 0;
}

void onTelnetConnect_Shell(String ip)
{
    spdlog::info("- Telnet: {}, connected", ip.c_str());
    telnet_shell.println("\nWelcome, your ip is: " + telnet_shell.getIP() + " connect port: " + telnet_shell_port);
    telnet_shell.println("> cmd bye to disconnect.");
    telnet_shell.println("> type help and ctrl + enter to get help.");
    is_telnet_shell_connected = 1;
}

void onTelnetDisconnect_Shell(String ip)
{
    spdlog::info(" onTelnetDisconnect_Shell disconnected");
    is_telnet_shell_connected = 0;
}

void onTelnetReconnect(String ip)
{
    spdlog::info("- Telnet: ");
    spdlog::info(ip);
    spdlog::info(" reconnected");
}

void onTelnetConnectionAttempt(String ip)
{
    spdlog::info("- Telnet: ");
    spdlog::info(ip);
    spdlog::info(" tried to connected");
}

// static telnetShellCmd[] = {
//     "bye", //
//     "test", //
// };

void onTelnetInput(String str)
{
    spdlog::info("telnet shell received: {}", str.c_str());

    if (str == "help")
    {
        telnet_shell.println("> list all cmd ...");
        telnet_shell.println("> ping");
        telnet_shell.println("> bye");
        telnet_shell.println("> cmd");
    }
    else if (str == "ping")
    {
        telnet_shell.println("> ping you...");
    }
    else if (str == "bye")
    {
        telnet_shell.println("> disconnecting you...");
        telnet_shell.disconnectClient();
    }
    else if (str == "cmd")
    {
        telnet_shell.println("> cmd...");
    }
    else
    {
        telnet_shell.print("> no match cmd such as: ");
        telnet_shell.println(str);
    }
}

void setupTelnet(void)
{
    telnet_shell.onConnect(onTelnetConnect_Shell);
    telnet_shell.onConnectionAttempt(onTelnetConnectionAttempt);
    telnet_shell.onReconnect(onTelnetReconnect);
    telnet_shell.onDisconnect(onTelnetDisconnect_Shell);
    telnet_shell.onInputReceived(onTelnetInput);
    if (telnet_shell.begin(telnet_shell_port))
    {
        spdlog::info("telnet_shell init successful on port: {}", telnet_shell_port);
    }
    else
    {
        spdlog::info("telnet_shell inti error.");
    }

    // telnet 2
    telnet_uart.onConnect(onTelnetConnect_Uart);
    // telnet_uart.onConnectionAttempt(onTelnetConnectionAttempt);
    // telnet_uart.onReconnect(onTelnetReconnect);
    telnet_uart.onDisconnect(onTelnetDisconnect_Uart);
    // telnet_uart.onInputReceived(onTelnetInput);
    // telnet_uart.onInputReceived([](String str)
    if (telnet_uart.begin(telnet_uart_port))
    {
        spdlog::info("telnet_uart  init successful on port: {}", telnet_uart_port);
    }
    else
    {
        spdlog::info("telnet_shell uart init error.");
    }
}

void telnet_send(String telnetPort = "Shell", String sendStr = "")
{
    if (g_netlog_status != NETWWORK_STATE_WIFI_CONNECTED)
    {
        return;
    }
    // spdlog::info("telnet_send, port:{}, str:{}", telnetPort.c_str(), sendStr.c_str());

    if (telnetPort == "Shell")
    {
        telnet_shell.println(sendStr);
    }
    else if (telnetPort == "Uart")
    {
        if (is_telnet_uart_connected == 0)
        {
            spdlog::info("telnetUart client is not connected");
            return;
        }
        telnet_uart.println(sendStr);
    }
    // else if (telnetPort == "Can")
    // {
    //     telnet_can.println(sendStr);
    // }
}

String wifiGetIpStr()
{
    IPAddress ip = WiFi.localIP();
    String ipStr = ip.toString();
    return ipStr;
}

String wifiGetSSid()
{
    String str = String(HAL::GetSystemConfig().wifi_ssid);
    return str;
}